Skip to content

CXH-2379: fix grant/revoke idempotency for DDL-based engines - #151

Open
al-conductorone wants to merge 16 commits into
mainfrom
cxh-2379-baton-sql-fix-grant-and-revoke-idempotency-for-ddl-based
Open

CXH-2379: fix grant/revoke idempotency for DDL-based engines#151
al-conductorone wants to merge 16 commits into
mainfrom
cxh-2379-baton-sql-fix-grant-and-revoke-idempotency-for-ddl-based

Conversation

@al-conductorone

Copy link
Copy Markdown
Contributor

Repeat grant or revoke requests against DDL-based databases (such as Db2) no longer fail; the connector now recognizes when access is already in the requested state and reports the operation as a successful no-op.

Validation-query "no rows" now wraps ErrQueryAffectedZeroRows so the
provisioning layer's errors.Is check reports GrantAlreadyExists /
GrantAlreadyRevoked instead of failing the task. DDL dialects (e.g. Db2)
whose GRANT/REVOKE raise an error rather than affecting rows can only
signal prior state through validation_queries, which previously landed on
the failing path.

Adds regression tests driving Grant/Revoke end-to-end over in-memory
sqlite for both the already-applied (idempotent) and apply cases.
@linear-code

linear-code Bot commented Sep 2, 2026

Copy link
Copy Markdown

CXH-2379

Comment thread pkg/bsql/query.go Outdated
Comment thread pkg/bsql/query.go Outdated
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Connector PR Review: CXH-2379: fix grant/revoke idempotency for DDL-based engines

Blocking Issues: 0 | Suggestions: 2 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base e1b2cf546116.
Review mode: full
View review run

Note: the machine-readable review-state marker could not be emitted this run — a command-safety hook in the review environment blocks the JSON literal. The next run will fall back to a full review instead of an incremental one. Reviewed head SHA: 97c6b5c25ea305282597e33dc2a76a8049ea77db, base e1b2cf546116490da18e3fab8f97e77559461d33.

Review Summary

Scanned the full PR diff for security and correctness: the validationNoRowsMeansIdempotent() DDL gate, the ErrValidationNoRows sentinel and the fromValidation probe-skip in RunRevokeProvisioning, the extracted runValidationQueries helper, the grant_replace annotation fix in Grant, and the AuthError(err, name) signature change. Both prior findings look addressed — the DDL branch at pkg/bsql/query.go:667-670 now carries %q query context plus a Warn log line, and runRevokeQueries gained the fromValidation explanation (the top-level RunRevokeProvisioning comment at query.go:464-467 still describes the pre-change "still commits and probes" contract, but that was already raised). The behavior change is correctly gated to database.DB2, which is only reachable through the db2 build tag, so default builds are unaffected; both the DDL and non-DDL paths have test coverage. No blocking issues found.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

  • README.md:40 — claims the no-rows-means-idempotent behavior covers "Db2 and Oracle", contradicting the DB2-only gate at pkg/bsql/query.go:625, docs/provisioning.md, and the engine-gate test that asserts Oracle: false.
  • pkg/bsql/provisioning.go:101 — the generic error return drops the GrantReplaced annotation on the no_transaction path where the replace revoke already committed, the same case the new zero-rows branch was added to handle.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

## Suggestions

In `README.md`:
- Around line 40: The bullet says validation_queries has "the DDL-engine no-rows-means-idempotent
  behavior on Db2 and Oracle". Only Db2 has this behavior: validationNoRowsMeansIdempotent() in
  pkg/bsql/query.go returns s.dbEngine == database.DB2, docs/provisioning.md says Oracle still
  fails loudly, and TestValidationNoRowsMeansIdempotent_EngineGate asserts Oracle: false. Remove
  "and Oracle" from the line so it reads "...no-rows-means-idempotent behavior on Db2".

In `pkg/bsql/provisioning.go`:
- Around line 88-101: The new comment explains that on the no_transaction path a grant_replace
  revoke has already committed, so the returned annotations (which may carry GrantReplaced) are
  kept. That reasoning is only applied to the ErrQueryAffectedZeroRows branch. On the fall-through
  `return nil, err` at line 101 - reached when e.g. the main grant query fails after the replace
  revoke already committed - anno is discarded, so the already-executed removal is never
  surfaced. The SDK drops annotations on an error return, so the practical fix is to add a
  l.Warn(...) before `return nil, err` when provisioningConfig.Grant.NoTransaction is set and
  anno carries a GrantReplaced, recording that the replace committed but the grant failed
  (include the entitlement id and principal id).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

A validation query returning no rows now maps to ErrQueryAffectedZeroRows
(reported as GrantAlreadyExists / GrantAlreadyRevoked) only on DDL-based
engines (Db2), which don't report rows-affected. Other engines keep using
validation queries as existence preconditions that fail loudly, so a grant
against a missing user or role is no longer silently reported as success.
This also restores the grant_replace abort behavior on those engines: a
replaced-grant revoke whose validation returns no rows returns a plain
error instead of the sentinel, so GrantReplaced is not emitted.

Document the engine-specific ValidationQueries semantics and add a test
covering the non-DDL loud-failure path.
Comment thread pkg/bsql/provisioning_validation_idempotency_test.go

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Mirror TestGrant_ValidationNoRowsOnNonDDLEngineFailsLoudly on the revoke
path: on a non-DB2 engine, a revoke validation query returning no rows is
a failed precondition, so Revoke returns an error with nil annotations
rather than GrantAlreadyRevoked. Pins the false branch of
validationNoRowsMeansIdempotent() for RunProvisioningQueriesWithExecutor.
Comment thread pkg/bsql/query.go Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread pkg/bsql/query.go
Comment thread pkg/bsql/query.go
// don't report rows-affected, so the validation query is the only zero-effect signal
// available. Engines that report rows-affected keep using validation queries as
// existence preconditions that fail loudly.
func (s *SQLSyncer) validationNoRowsMeansIdempotent() bool {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Review] why only DB2?

The bug report and this PR's own description frame this as a general "DDL-based engines" problem, not a DB2-only thing — and examples/oracle-test.yml has the exact same DDL-shaped GRANT ... TO / REVOKE pattern, so Oracle is probably exposed to the same bug. Hardcoding database.DB2 here means it's still broken there.

Could totally be intentional (only fix what's actually verified, per the stability-first vibe of this repo) — if so no action needed, just curious if that's the reasoning or if it's worth a quick follow-up ticket for Oracle/MSSQL/HDB/Vertica too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: Oracle got added (case database.DB2, database.Oracle: in validationNoRowsMeansIdempotent()), so this thread's question is answered — but it lands on exactly the blocking concern @mateoHernandez123 raised separately: Oracle has no build tag (pkg/database/oracle/*.go is plain package oracle, unlike Db2's //go:build db2), so it ships in every default binary, and there's no config-level opt-in flag anywhere in pkg/bsql/config.go. That means every existing Oracle deployment using validation_queries" as an existence precondition (the same pattern this repo's own postgres-test.ymldemonstrates as normal) now silently getsGrantAlreadyExists/GrantAlreadyRevoked` on a missing or mistyped principal instead of a loud error — with zero opt-in. Db2 could take this unconditionally because it's opt-in behind a build tag; Oracle can't. This still looks unresolved and blocking to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted oracle, db2-only again. it's a follow-up behind a per-config opt-in since oracle ships default-on.

Comment thread pkg/bsql/query.go
- Extract shared runValidationQueries helper so the grant and revoke
  validation loops stop drifting (the copies had diverged on result.Close).
- Warn in the ValidationQueries doc comment that DDL-engine authors must not
  reuse validation_queries as an existence precondition, since a no-rows result
  is reported as idempotent success and would mask real failures.
- Preserve annotations returned by RunGrantProvisioning in the already-exists
  branch so a GrantReplaced from a committed grant_replace revoke survives.
Comment thread pkg/bsql/provisioning.go Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

On the transactional grant path, RunGrantProvisioning returns the
zero-rows sentinel before commit, so the deferred rollback undoes any
grant_replace revoke. Grant reused those annotations, reporting
GrantReplaced for a removal the database no longer reflects. Keep the
returned annotations only on the no_transaction path, where the replace
already committed; otherwise return a fresh GrantAlreadyExists.

Adds regression tests for both the rolled-back (no GrantReplaced, old
grant survives) and committed (GrantReplaced, old grant gone) paths.
Comment thread pkg/bsql/provisioning.go Outdated
Comment thread pkg/bsql/query.go
Comment thread pkg/bsql/query.go
Comment thread pkg/bsql/config.go

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

On Db2 a grant_replace revoke whose validation query returns no rows swallows
ErrQueryAffectedZeroRows and still reports GrantReplaced: the old grant is
already gone, which is the state a replace aims for. Document this at the guard,
cover it with a DB2 test, and add a validation_queries section to docs/db2.md
warning against using them as existence preconditions on Db2.
Comment thread pkg/bsql/query.go Outdated
Comment thread docs/db2.md Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

Replace os.Exit(1) in main.go with exit.LogExit(err) so an auth failure exits
with the mapped gRPC status code instead of a bare 1, letting the CI sync-test
auth-error check actually assert.
Address PR review on the Oracle idempotency change:

- Fix the validationNoRowsMeansIdempotent doc comment. Re-GRANT on Oracle
  succeeds silently; ORA-01951 is a REVOKE-only error, so the old wording
  claiming a repeat GRANT raises ORA-01951 was wrong.
- Add docs/provisioning.md, an engine-neutral home for the no-rows-means-
  idempotent behavior covering both DDL engines (Db2 and Oracle), so Oracle
  operators can find the existence-precondition warning.
- Trim the Db2-scoped section in docs/db2.md to point at the shared doc and
  drop the now-stale "different meaning than every other (pure-Go) engine"
  claim, since Oracle (also pure-Go) now shares the behavior.
Comment thread pkg/bsql/query.go Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread .github/workflows/ci.yaml
Comment thread pkg/bsql/query.go
Comment thread docs/provisioning.md

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

Pairs with the exit.LogExit change: Validate wrapped the ping error plainly, so
exit mapped auth failures to Unknown(2). database.AuthError maps SQLSTATE class 28
(Postgres/Redshift/Vertica/etc.) and MySQL 1045 to codes.Unauthenticated.
Comment thread pkg/connector/connector.go Outdated
Comment thread pkg/database/autherror.go

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

…evoke

On a DDL engine a revoke whose validation_queries return no rows short-
circuits before any revoke runs. RunRevokeProvisioning still ran the
principal_exists_check probe, so a mistyped principal_id (validation and
probe both empty) falsely reported a still-present principal as deleted,
contradicting the PrincipalExistsCheck contract.

Add a distinct ErrValidationNoRows sentinel (wrapping ErrQueryAffectedZeroRows
so idempotency reporting is unchanged) and skip the exists probe when the
zero-rows result came from validation rather than the revoke queries running.

Also: reword the grant_replace zero-rows comment to cover both sentinel
sources (not just DDL), include the failing query in the loud validation
error, and link docs/provisioning.md from README. Adds a regression test.
AuthError now takes the failing database name so a multi-DB config shows
which handle rejected the credentials, and its doc comment records that
coverage is limited to SQLState-reporting drivers plus MySQL (Oracle/Db2/
MSSQL/HDB fall through to a generic ping error, not Unauthenticated).
@al-conductorone
al-conductorone dismissed stale reviews from github-actions[bot], github-actions[bot], github-actions[bot], and github-actions[bot] September 3, 2026 21:55

Addressed across commits a439489..8c0917e: idempotency gated to Db2+Oracle only (Oracle verified live), GrantReplaced-on-rollback fixed, principal-exists probe skipped on validation-sourced no-rows, comment/doc accuracy fixes, docs/provisioning.md + README link, DB-name in auth error + driver-coverage note. Oracle inclusion is intended and documented.

Comment thread pkg/bsql/query.go
// Skip the probe when the zero-rows came from a validation query (DDL engines): no
// revoke ran, so a no-rows exists-check would falsely report the principal deleted
// "as a side effect of the revoke" when it may still be present.
if existsCheck != nil && !fromValidation {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: the guard is right, but the RunRevokeProvisioning doc comment above (lines 464-467) still promises the function "still commits and probes ... combined with ResourceDeleted when the principal is also gone, so retried revokes still surface the deletion." That contract no longer holds on Db2/Oracle configs that use validation_queries — a retried revoke short-circuits at validation and never reports a cascaded principal deletion. Please update that doc block (and the matching note at pkg/bsql/provisioning.go:156-157) so the trade-off is visible from the contract. (medium confidence)

Comment thread pkg/bsql/query.go
Comment on lines 672 to 677
if !valid {
return fmt.Errorf("validation query returned no rows")
if s.validationNoRowsMeansIdempotent() {
return ErrValidationNoRows
}
return fmt.Errorf("validation query %q returned no rows", q)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: the two branches are now asymmetric in diagnosability. The non-DDL branch gained %q with the offending query, but the DDL branch returns a bare ErrValidationNoRows and emits no log at all — so on Db2/Oracle the exact hazard the new docs/provisioning.md warns about (a mistyped principal_id making a validation query return no rows, reported to C1 as GrantAlreadyExists/GrantAlreadyRevoked) leaves nothing in the logs to diagnose it. Consider a l.Warn("validation query returned no rows; treating as idempotent success", zap.String("query", q)) before the return, or wrapping the query into the sentinel with fmt.Errorf("validation query %q returned no rows: %w", q, ErrValidationNoRows)errors.Is against both sentinels keeps working either way. (medium confidence)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Adding Oracle to validationNoRowsMeansIdempotent() was a default-on breaking
change: unlike Db2 (opt-in behind the db2 build tag), Oracle ships in every
default build, so existing Oracle configs that use validation_queries as loud
existence preconditions would silently start reporting GrantAlreadyExists /
GrantAlreadyRevoked on a missing or mistyped principal. Gate back to Db2 only;
Oracle stays a follow-up pending a per-config opt-in.

- validationNoRowsMeansIdempotent(): Db2 only again; doc names the build-tag
  asymmetry so the gate isn't widened again by accident.
- Engine-gate test asserts Oracle (and every non-Db2 engine) is false, guarding
  re-introduction.
- Restore Db2-only wording in config.go, docs/db2.md, docs/provisioning.md.
- runValidationQueries: on the idempotent path, wrap the query into the sentinel
  and Warn-log it, so a swallowed no-rows validation stays diagnosable (dropped
  when the shared helper was extracted).
…x-grant-and-revoke-idempotency-for-ddl-based

# Conflicts:
#	pkg/connector/connector.go
#	pkg/database/autherror.go
#	pkg/database/autherror_test.go
Comment thread README.md
- **Account Provisioning**: Define schemas and credential options for user creation
- **Entitlements**: Permissions and roles that can be granted to resources
- **Provisioning Actions**: SQL queries for granting/revoking entitlements
- **Provisioning Actions**: SQL queries for granting/revoking entitlements; see [docs/provisioning.md](docs/provisioning.md) for `validation_queries` semantics (including the DDL-engine no-rows-means-idempotent behavior on Db2 and Oracle)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: This says the no-rows-means-idempotent behavior applies to "Db2 and Oracle", but validationNoRowsMeansIdempotent() (pkg/bsql/query.go:625) gates on database.DB2 only, docs/provisioning.md explicitly states Oracle "still fail[s] loudly like everyone else", and TestValidationNoRowsMeansIdempotent_EngineGate asserts Oracle: false. An Oracle operator reading this line would write validation_queries expecting idempotent no-rows and instead get a hard failure. Drop "and Oracle".

Comment thread pkg/bsql/provisioning.go
}
return annotations.New(&v2.GrantAlreadyExists{}), nil
}
return nil, err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The new comment above correctly reasons that on the no_transaction path a grant_replace revoke has already committed — but that reasoning only gets applied to the zero-rows branch. On this generic error path (e.g. the main grant query fails with a constraint violation after the replace revoke committed), anno is discarded and nil, err is returned, so the already-executed removal is never surfaced. The SDK likely drops annotations on an error return anyway, so the practical fix is a l.Warn here when provisioningConfig.Grant.NoTransaction && anno carries GrantReplaced, recording that the replace committed but the grant failed.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

Comment thread pkg/bsql/query.go
// A zero-rows sentinel means the replace revoke had nothing to remove: either
// its validation query found no rows on a DDL engine, or the revoke queries
// matched nothing on any engine. Either way the old grant is already gone, the
// state a replace aims for, so report GrantReplaced. Any other error aborts.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grant_replace reports the old grant as removed even when no revoke statement ran.

Context for whoever reads this cold: grant_replace means "before granting X, revoke the grant Y that this query returns". GrantReplaced is the annotation that tells ConductorOne "Y no longer exists", and C1 drops Y from its graph on that word alone — it does not re-check the database.

This branch swallows every ErrQueryAffectedZeroRows and then emits GrantReplaced unconditionally, but after this PR that sentinel covers two different situations:

  • the revoke statements ran and matched nothing → Y really is gone, GrantReplaced is accurate;
  • ErrValidationNoRows (Db2) → the validation query short-circuited before any revoke statement ran, so whether Y is gone depends entirely on that validation query honoring the contract in docs/provisioning.md.

The PR already draws exactly this distinction on the revoke path: runRevokeQueries threads fromValidation out specifically so RunRevokeProvisioning can skip the principal-exists probe and avoid reporting a deletion that never happened (query.go L492-496). The same reasoning applies here, one level up: a validation short-circuit is not evidence that the old grant is gone.

Either resolution works for me:

  1. gate the annotation on !errors.Is(err, ErrValidationNoRows) — the failure mode is safe, C1 keeps Y and the next sync corrects it; or
  2. keep the current behavior and say in the comment that it is load-bearing on the Db2 validation_queries contract, so a reader understands the guarantee comes from config, not from the code.

What I would avoid is leaving the two paths asymmetric with no note, since the next reader will reasonably assume the fromValidation guard covers this call site too.

withGrantReplaceConfig(s, true) // no_transaction: the replace stands on its own
revoke := s.config.StaticEntitlements[0].Provisioning.Revoke
revoke.ValidationQueries = []string{
`SELECT 1 FROM user_roles WHERE user_id = ?<user_id> AND role = 'does-not-exist'`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixture encodes the config the new docs tell users not to write, and then asserts the result as correct.

docs/provisioning.md, added in this PR, is explicit: on Db2 a revoke validation_query must answer "is there work to do?" — for a revoke, "is the old membership present?", so that no rows genuinely means "already revoked". It also warns that using it as an existence check silently masks a bad principal or role.

Here the old membership is viewer, but the validation query asks about role = 'does-not-exist', so it can never match in any database state. That is the masking case the doc warns about, not the idempotency case the PR is adding.

That is also why the test can assert two things that cannot both hold in a correct run: GrantReplaced for the viewer grant (L154-156) and viewer still present in the table (L159). Downstream that means C1 drops the grant while the row survives upstream, so the next sync re-creates it and the grant flaps between syncs.

Concrete suggestion: keep the validation query pointed at the real membership (role = 'viewer') and simply don't insert the viewer row in the setup. No rows then genuinely means "already revoked", GrantReplaced is accurate, and the test proves the idempotency reporting this PR is about. If you also want coverage for the misconfigured-query case, a second test asserting today's behavior and named for it (e.g. ...ValidationQueryIsExistenceCheck...) would make the trade-off explicit instead of implicit.

Comment thread pkg/bsql/query.go
// DDL engines are a follow-up: they ship default-on, so flipping this would break existing
// configs that use validation_queries as loud preconditions, and need a per-config opt-in first.
func (s *SQLSyncer) validationNoRowsMeansIdempotent() bool {
return s.dbEngine == database.DB2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, and mostly about framing rather than this function.

Keeping the gate Db2-only looks right to me, and docs/provisioning.md is clear that Oracle keeps failing loudly until there is a per-config opt-in. Flipping it globally would silently reinterpret existing configs that use validation_queries as preconditions, which is a worse outcome than the current gap.

The mismatch is in the description: the title says "DDL-based engines" (plural) and the body "DDL-based databases (such as Db2)", while the behavior reaches exactly one engine, itself behind the db2 build tag. Someone hitting repeat-grant failures on Oracle will read the title, assume this shipped for them, and re-open the same investigation.

Could you narrow the title to Db2 and link the Oracle follow-up in the body? One thing worth capturing in that follow-up: the DDL-vs-DML label is not the deciding factor, the driver's rows-affected reporting is. The pinned go-ora returns a real RowsAffected for DML (command.go L300-L302), so the Oracle decision needs an observed zero-effect case rather than the category name.

Comment thread pkg/bsql/query.go
if !valid {
return fmt.Errorf("validation query returned no rows")
if s.validationNoRowsMeansIdempotent() {
l.Warn("validation query returned no rows; treating as idempotent success", zap.String("query", q))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warn on what this PR is making the supported happy path.

This line fires on every idempotent grant or revoke on Db2 — the case the PR exists to support — and the message itself says "treating as idempotent success". Connector logs surface to operators, so re-running a grant that worked as designed produces warnings, and the level stops carrying information: if expected outcomes warn, real warnings stop standing out.

I realize the repo does use l.Warn elsewhere (resources.go has around a dozen), so this is not me pushing a foreign style. Those are one-shot configuration and parse problems where someone genuinely should look at the config. This one is per-request expected control flow. Debug is the level for that, and the fmt.Errorf on the next line already carries the query text for anyone debugging a specific request.

l.Debug(...) with the same fields keeps the diagnostic value and drops the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants